const $_ = require('lodash');

// websocket.readyState
// 0 (WebSocket.CONNECTING) 正在链接中
// 1 (WebSocket.OPEN) 已经链接并且可以通讯
// 2 (WebSocket.CLOSING) 连接正在关闭
// 3 (WebSocket.CLOSED) 连接已关闭或者没有链接成功
let WSK = function(opts) {
  this.name = opts.name || '1';
  this.host = opts.host || 'ws://127.0.0.1';
  this.isAutoLink = $_.isBoolean(opts.isAutoLink) ? opts.isAutoLink : false; //允许断线重连

  //指定发包开头标识，默认为<BEGIN>，   空str:true
  //指定心跳包字符串，默认为__HEART__， 空str:false
  //指定发包结尾标识，默认为<END>，     空str:flase
  this.strBegin = $_.isString(opts.strBegin) ? opts.strBegin : '<BEGIN>';
  this.strHeart =
    $_.isString(opts.strHeart) && Boolean(opts.strHeart)
      ? opts.strHeart
      : '__HEART__';
  this.strEnd =
    $_.isString(opts.strEnd) && Boolean(opts.strEnd) ? opts.strEnd : '<END>';

  this.timeHeart = opts.timeHeart || 1000 * 60; //心率
  this.wsObj = opts.wsObj || {};
  this.autoLinkMax = opts.autoLinkMax || 65535;

  this.socket = null;
  /*4个事件+前后共8个回调函数*/
  this.beforeOpen = $_.isFunction(opts.beforeOpen) ? opts.beforeOpen : () => {};
  this.afterOpen = $_.isFunction(opts.afterOpen) ? opts.afterOpen : () => {};
  this.beforeClose = $_.isFunction(opts.beforeClose)
    ? opts.beforeClose
    : () => {};
  this.afterClose = $_.isFunction(opts.afterClose) ? opts.afterClose : () => {};
  this.beforeError = $_.isFunction(opts.beforeError)
    ? opts.beforeError
    : () => {};
  this.afterError = $_.isFunction(opts.afterError) ? opts.afterError : () => {};
  this.beforeMessage = $_.isFunction(opts.beforeMessage)
    ? opts.beforeMessage
    : () => {};
  this.afterMessage = $_.isFunction(opts.afterMessage)
    ? opts.afterMessage
    : () => {};
  this.beforeSendHeart = $_.isFunction(opts.beforeSendHeart)
    ? opts.beforeSendHeart
    : () => {};
  this.afterGetHeart = $_.isFunction(opts.afterGetHeart)
    ? opts.afterGetHeart
    : () => {};
  /*调用时都传入两个参数：1、当前对象obj；2、当前事件event*/
  return this;
};

function handleOpen($event) {
  this.__autoLink_num = 0;
  clearTimeout(this.__autoLink_t);
  //重置自动重连的计数器
  this.send(this.wsObj);
  let self = this;
  setTimeout((_) => {
    self._$heart(true);
  }, this.timeHeart); //延迟一个周期启动心跳
}
function isHeart(str) {
  return str === `${this.strBegin}${this.strHeart}${this.strEnd}`;
}
function sortingAbnormal(str) {
  //sorting:分拣 abnormal:异常
  //没有异常返回 0 ，异常返回{message:errorMsg,strMsg}
  let α = this.strBegin;
  let Ω = this.strEnd;
  if (Boolean(α) && str.indexOf(α) == 0) {
    return { type: 1, message: '消息不以约定的消息头开始', json: null };
  }
  if (str.lastIndexOf(Ω) != str.length - Ω.length) {
    return { type: 2, message: '消息不以约定的消息尾结束', json: null };
  }
  let json;
  try {
    let strMain = str.substring(α.length, str.lastIndexOf(Ω));
    if (strMain == this.strHeart) {
      return { type: 0 };
    }
    json = JSON.parse(strMain);
  } catch (err) {
    console.error(err);
    return { type: 4, message: '消息主体无法解析为JSON格式', json: null };
  }
  return { type: 0, json: json };
}
function handleMessage($event, msg) {}
function handleClose($event) {
  // CloseEvent有三个字段需要注意, 通过分析这三个字段，一般就可以找到断开原因
  // CloseEvent.code: code是错误码，是整数类型
  // CloseEvent.reason: reason是断开原因，是字符串
  // CloseEvent.wasClean: wasClean表示是否正常断开，是布尔值。一般异常断开时，该值为false
  if ($event.code != 1000) {
    this._$autoLink(); //尝试自动重连
  }
  // if (!$event.wasClean) {
  //   this.stop();
  //   this.socket = null;
  // }
}
function handleError($event) {
  // $_.debounce(
  //   this._$autoLink(), //尝试自动重连
  //   1000
  // )
}

WSK.prototype = {
  __tmpSend: [], //发送缓存队列
  __tmpReceive: '', //接受缓存队列(实际是字符串)
  __autoLink_t: {}, //--重试定时器
  __autoLink_num: 0, //--重复计数器--
  start: function() {
    let self = this;
    this.socket = new WebSocket(this.host);
    this.socket.onopen = function(e) {
      // console.clear()
      self.beforeOpen(self, e);
      handleOpen.call(self, e);
      self.afterOpen(self, e);
    };
    this.socket.onclose = function(e) {
      // self.beforeClose(self, e)
      handleClose.call(self, e);
      self.afterClose(self, e);
    };
    this.socket.onmessage = function(e) {
      let strMsg = e.data;
      new Promise((res, rej) => {
        let r = self.beforeMessage(self, strMsg, e);
        res(strMsg); //未经过心跳分拣的
      })
        .then((res) => {
          if (isHeart.call(self, res)) {
            self.afterGetHeart(self);
            return Promise.resolve({ type: 'heart' });
            //心跳可能性较高，提前处理直接返回指定值，后续检测指定值&&不执行afterMessage
          } else {
            let r = sortingAbnormal.call(self, strMsg);
            if (r.type != 0) {
              return Promise.reject(r);
            } else {
              return Promise.resolve(r);
            }
          }
        })
        .then((res) => {
          if (res.type === 'heart') {
          } else {
            handleMessage.call(self, e, res.json);
            self.afterMessage(self, res.json, e);
          }
        });
    };
    this.socket.onerror = function(e) {
      // self.beforeError(self, e);
      // debounced
      // throttle
      // $_.throttle(
      //   self._$autoLink(), 1000
      // );//尝试自动重连
      handleError.call(self, e);
      // self.afterError(self, e);
    };
    return this;
  }, //start
  stop: function(e) {
    let self = this;
    self.isAutoLink = false;
    //手动终止自动重连
    clearTimeout(self.__autoLink_t);
    //清空队列
    self.__tmpSend = [];
    self.__tmpReceive = [];
    // self.beforeClose(self, e);
    try {
      self.socket.close();
      self.socket = null;
    } catch (err) {
      console.error(err);
    }
    // self.afterClose(self, e);
    return this;
  }, //stop
  send: function(msgObj) {
    //指定的信息使用websocket对象原生send发送
    //未指定的信息使用封装的方式发送
    //按要求还要规避粘包，引入__tmp
    if ($_.isNil(msgObj)) {
      return false;
    }
    let self = this;
    let msg = '';

    // self.wsObj[] = ;
    try {
      let obj = Object.assign({}, self.wsObj, msgObj);
      msg = self.strBegin + JSON.stringify(obj) + self.strEnd;
    } catch (err) {
      msg = msgObj.toString();
    }
    this.__tmpSend.push(msg); //缓存队列增加信息列表
    // 数组去重

    for (var i = 0; i < this.__tmpSend.length; i++) {
      for (var j = i + 1; j < this.__tmpSend.length; j++) {
        let data_1 = this.__tmpSend[i].split(
          `${this.strEnd}${this.strBegin}`
        )[0];
        data_1 = JSON.parse(data_1);
        let data_2 = this.__tmpSend[j].split(
          `${this.strEnd}${this.strBegin}`
        )[0];
        data_2 = JSON.parse(data_2);
        if (data_1.msgBody == data_2.msgBody) {
          //第一个等同于第二个，splice方法删除第二个
          this.__tmpSend.splice(j, 1);
          j--;
        }
      }
    }

    if (self.__t === undefined) {
      //这个变量没有声明过……
      self.__t = setInterval(function() {
        if (self.socket == null) return;
        // https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket/bufferedAmount
        if (self.socket.bufferedAmount == 0) {
          //要求发送前bufferedAmount必须为空
          if (self.__tmpSend.length > 0) {
            //缓存队列尚有未发消息
            try {
              //尝试发送消息
              self.socket.send(self.__tmpSend[0]);
            } catch (err) {
              console.error(err);
            }
            self.__tmpSend.splice(0, 1); //移除已发送
          } else {
            //缓存队列以空
            clearInterval(self.__t);
            self.__t = undefined;
            //清空定时器，判断条件复位
          }
        }
      }, 20);
    }
    return this;
  }, //send
  _$heart: function(boolean) {
    let self = this;
    if (self.socket == null) return;
    let readyState = $_.get(this.socket, 'readyState', []);
    if ([2, 3].includes(readyState)) {
      return false;
    } //既然已经关闭了就没必要发心跳包
    if (boolean) {
      this.beforeSendHeart(this);
      let heartBox = `${this.strBegin}${this.strHeart}${this.strEnd}`;
      try {
        this.socket.send(heartBox); //指定的心跳标识和结束标识
      } catch (err) {
        throw err;
      }
    }
    setTimeout(function() {
      self._$heart(true);
    }, self.timeHeart);
    return this;
  }, //心跳包
  _$autoLink: function() {
    let self = this;
    console.log(
      self.__autoLink_num,
      self.autoLinkMax,
      self.__autoLink_num > self.autoLinkMax
    );

    if (this.isAutoLink) {
      self.__al_time = self.__al_time ? self.__al_time : 3000; //这个变量没有声明过……
      self.__autoLink_t = setTimeout(function() {
        self.__autoLink_num++;
        if (self.__autoLink_num > 2) {
          self.__al_time = 5000;
        }
        if (self.__autoLink_num > self.autoLinkMax) {
          self.isAutoLink = false;
          console.log('放弃尝试....');
          self.socket.close();
          return false;
        }
        console.log(`[${self.__autoLink_num}/${self.autoLinkMax}]尝试重连...`);
        self.start();
      }, self.__al_time);
    } else {
      console.log('stop');
      this.stop();
    }
  }, //自动重连
};
export default WSK;
